﻿using System;
using System.Text;
using System.Web.Hosting;

namespace OpenWaves.ImageTransformations.Web
{
    public class WebImageTransformationService : IWebImageTransformationService
    {
        private const string PngExtension = ".png";
        private const string GifExtension = ".gif";
        private const string JpgExtension = ".jpg";

        private readonly IVirtualFileProvider imageFileProvider;
        private readonly IConcurrentFileStore transformedImageStore;
        private readonly IImageTransformationService imageTransformationService;

        #if NET_40_OR_GREATER

        public WebImageTransformationService() :this(
            new VirtualPathFileProvider(HostingEnvironment.VirtualPathProvider),
            new ConcurrentFileStore(new MapPathBasedFileStore(UrlPath.Parse("~/Images/Scaled"))), 
            new GdiImageTransformationService())
        {
        }

        #endif

        public WebImageTransformationService(IVirtualFileProvider imageFileProvider, IConcurrentFileStore scaledImageStore, IImageTransformationService imageTransformationService)
        {
            this.imageFileProvider = imageFileProvider;
            this.transformedImageStore = scaledImageStore;
            this.imageTransformationService = imageTransformationService;
        }

        public Url GetTransformedImageUrl(Url imageUrl, IImageTransformation transformation)
        {
            try
            {
                var inputFile = this.imageFileProvider.GetFile(imageUrl);
                if (inputFile == null)
                {
                    var message = ExceptionMessage.Format(
                        "Failed to locate file at '{0}'. Ensure that registered path provider ({1}) can resolve the url. Usually '{2}' is registered in Global.asax.cs.",
                        imageUrl, this.imageFileProvider, typeof(IVirtualFileProvider));

                    throw new ImageTransformationException(message, imageUrl, transformation);
                }

                var sourceExtension = inputFile.Url.Path.GetFileExtension();
                var outputFormat = GetImageFormatFromExtension(sourceExtension);
                var resultExtension = GetExtensionFromImageFormat(outputFormat);

                var transformedImagePath = GetTransformedImagePath(inputFile, transformation, resultExtension);

                return this.transformedImageStore.GetOrCreateFileUrl(
                    transformedImagePath, 
                    TimeSpan.FromSeconds(10),
                    transformedImageStream =>
                        {
                            using (var inputFileStream = inputFile.Open())
                            {
                                this.imageTransformationService.TransformImage(
                                    inputFileStream,
                                    transformedImageStream,
                                    outputFormat,
                                    transformation);
                            }

                        });
            }
            catch (Exception e)
            {
                var message = ExceptionMessage.Format("Failed to apply transformation to an image. ImageUrl: {0}, Transformation: {1}",
                                                      imageUrl, transformation);

                throw new ImageTransformationException(message, imageUrl, transformation, e);
            }
        }

        private static string GetExtensionFromImageFormat(ImageFormat format)
        {
            switch (format)
            {
                case ImageFormat.Jpeg:
                    return JpgExtension;
                case ImageFormat.Png:
                    return PngExtension;
                default:
                    throw new NotSupportedException("Only Jpeg and Png formats are supported due to fine quality/size ratio.");
            }
        }

        private static ImageFormat GetImageFormatFromExtension(string extension)
        {
            if (extension.Equals(PngExtension, StringComparison.OrdinalIgnoreCase) || 
                extension.Equals(GifExtension, StringComparison.OrdinalIgnoreCase))
            {
                return ImageFormat.Png;
            }

            //for other extensions use jpeg (expecially WMF, BMP, ...)
            return ImageFormat.Jpeg;
        }

        private static string GetTransformedImagePath(IVirtualFile imageFile, IImageTransformation transformation, string extension)
        {
            var sb = new StringBuilder();
            var name = sb
                .Append(imageFile.Url)
                .Append(imageFile.Hash ?? String.Empty)
                .Append(transformation)
                .ToString();

            return MD5Hash.Compute(name) + extension;
        }
    }
}